package com.imooc.controller;

import com.imooc.enums.OrderStatusEnum;
import com.imooc.enums.PayMethod;
import com.imooc.pojo.OrderStatus;
import com.imooc.pojo.bo.ShopcartBO;
import com.imooc.pojo.bo.SubmitOrderBO;
import com.imooc.pojo.vo.MerchantOrdersVO;
import com.imooc.pojo.vo.OrderVO;
import com.imooc.service.OrderService;
import com.imooc.utils.CookieUtils;
import com.imooc.utils.IMOOCJSONResult;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.RedisOperator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("orders")
@Api(value = "订单相关", tags = {"订单相关的api接口"})
public class OrdersController extends BaseController {

    @Autowired
    private OrderService orderService;

    //我们可以使用restTemplates在java代码里发送一个restful请求,springmvc就有这个restTemplate了
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisOperator redisOperator;

    @Autowired
    private RedissonClient redissonClient;

    @Bean
    public RedissonClient redissonClient() {
        // 1. Create config object
        Config config = new Config();
        config.useSingleServer()
                // use "rediss://" for SSL connection
                .setAddress("redis://192.168.124.140:6379").setPassword("imooc");

        // 2. Create Redisson instance

// Sync and Async API
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }

    /**
     * 提交订单接口的幂等性验证
     *
     * @param session
     * @return
     */
    @ApiOperation(value = "获取订单token", notes = "建立并获取订单token,保证提交订单的接口幂等性", httpMethod = "POST")
    @PostMapping("/getOrderToken")
    public IMOOCJSONResult getOrderToken(HttpSession session) {
        String token = UUID.randomUUID().toString();
        //将token存在redis,这里我们的粒度是sessionId,
        // 比如用户在两个浏览器下单,那么他其实是意识下两单的,防止的是一个浏览器多次下单过程
        redisOperator.set("ORDER_TOKEN_" + session.getId(), token, 600);
        return IMOOCJSONResult.ok(token);
    }

    @ApiOperation(value = "用户下单", notes = "用户下单", httpMethod = "POST")
    @PostMapping("/create")
    public IMOOCJSONResult create(@RequestBody SubmitOrderBO submitOrderBO, HttpServletRequest request,
                                  HttpServletResponse response) {

        /**
         * 校验token有没有
         */
        String orderTokenKey = "ORDER_TOKEN_" + request.getSession().getId();

        //并发情况下需要给这个操作加锁,我们用另一个字段加锁
        String lockKey = "LOCK_KEY_" + request.getSession().getId();
        RLock lock = redissonClient.getLock(lockKey);
        //锁住五秒保证用户下单方法运行完成
        lock.lock(5, TimeUnit.SECONDS);

        try {
            String orderToken = redisOperator.get(orderTokenKey);
            if (StringUtils.isBlank(orderToken)) {
                throw new RuntimeException("orderToken不存在");
            }
            boolean correctToken = orderToken.equals(submitOrderBO.getToken());
            if (!correctToken) {
                throw new RuntimeException("orderToken不正确");
            }
            //我们要做幂等性,执行一次成功后就要销毁token,不然这个token验证始终能通过
            redisOperator.del(orderTokenKey);
        } finally {
            //orderTokenKey锁释放
            try {
                lock.unlock();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 经上述token校验,通过后执行如下逻辑
         */

        System.out.println(submitOrderBO);
        if (submitOrderBO.getPayMethod() != PayMethod.WEIXIN.type && submitOrderBO.getPayMethod() != PayMethod.ALIPAY.type) {
            return IMOOCJSONResult.errorMsg("支付方式不支持");
        }

        String shopcartJson = redisOperator.get(FOODIE_SHOPCART + ":" + submitOrderBO.getUserId());
        if (StringUtils.isBlank(shopcartJson)) {
            return IMOOCJSONResult.errorMsg("购物数据不正确");
        }
        List<ShopcartBO> shopcartList = JsonUtils.jsonToList(shopcartJson, ShopcartBO.class);

        //1.创建订单
        OrderVO orderVO = orderService.createOrder(shopcartList, submitOrderBO);
        String orderId = orderVO.getOrderId();
        //我们还没有设置返回returnUrl可以在这里设置

        //2.创建订单后,移除购物车中已经结算的商品
        //  已完成TODO 这里需要我们整合redis之后去完善,我们这里直接清空购物车就可以
        //购物车结算商品清除,同步到cookie,覆盖redis
        shopcartList.removeAll(orderVO.getToBeRemovedShopcatdList());
        redisOperator.set(FOODIE_SHOPCART + ":" + submitOrderBO.getUserId(), JsonUtils.objectToJson(shopcartList));

        CookieUtils.setCookie(request, response, FOODIE_SHOPCART, JsonUtils.objectToJson(shopcartList), true);
        //3.向支付中心发送当前订单,用于保存支付中心的订单数据
        MerchantOrdersVO merchantOrdersVO = orderVO.getMerchantOrdersVO();
        merchantOrdersVO.setReturnUrl(payReturnUrl);
        //为了方便测试统一设置为1分钱
        merchantOrdersVO.setAmount(1);
        //要穿一个对象参数,我们需要构建JSON对象,先new 一个httpHeader对象
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add("imoocUserId", "3372580-752164156");
        headers.add("password", "rr4i-290i-ir09-23i9");
        HttpEntity<MerchantOrdersVO> entity = new HttpEntity<>(merchantOrdersVO, headers);
        ResponseEntity<IMOOCJSONResult> responseEntity =
                restTemplate.postForEntity(paymentUrl, entity, IMOOCJSONResult.class);
        IMOOCJSONResult paymentResult = responseEntity.getBody();
        if (paymentResult.getStatus() != 200) {
            return IMOOCJSONResult.errorMsg("支付中心创建订单错误,请联系管理员!");
        }
        return IMOOCJSONResult.ok(orderId);
    }

    @PostMapping("/notifyMerchantOrderPaid")
    public Integer notifyMerchantOrderPaid(String merchantOrderId) {
        //这个由支付中心通知我们后台程序修改数据库
        orderService.updateOrderStatus(merchantOrderId, OrderStatusEnum.WAIT_DELIVER.type);
        return HttpStatus.OK.value();
    }

    //这个是前端轮训订单状态的接口
    @PostMapping("/getPaidOrderInfo")
    public IMOOCJSONResult getPaidOrderInfo(@RequestParam String orderId) {
        OrderStatus orderStatus = orderService.queryOrderStatusInfo(orderId);
        return IMOOCJSONResult.ok(orderStatus);
    }
}